package org.locker.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.service.WxPayService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.locker.common.core.domain.R;
import org.locker.common.core.exception.ServiceException;
import org.locker.common.core.utils.StringUtils;
import org.locker.common.oss.constant.OssConstant;
import org.locker.domain.*;
import org.locker.domain.enums.OpenSpiltEnum;
import org.locker.domain.enums.OrderPaySpiltStatusEnum;
import org.locker.domain.enums.OrderPayWayEnum;
import org.locker.domain.enums.OrderStatusEnum;
import org.locker.netty.MachineService;
import org.locker.service.IOrderService;
import org.locker.service.IPaymentService;
import org.locker.service.IPositionService;
import org.locker.service.IUserService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * @Description: 支付
 * @Author: lark
 * @Date: 2023/10/25
 */

@RequiredArgsConstructor
@Service
@Slf4j
public class PaymentServiceImpl implements IPaymentService {

    private final IOrderService orderService;
    private final IUserService userService;
    private final WxPayService wxPayService;
    private final MachineService machineService;
    private final IPositionService positionService;



    @Value("${wx.pay.notify_url}")
    private String notifyUrl;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public R orderPay(String orderNo,String ip) {
        Order order = orderService.getByOrderNo(orderNo);
        if (ObjectUtil.isNull(order)) {
            throw new ServiceException(StrUtil.format("订单号: {}不存在!",orderNo));
        }
        String orderStatus = order.getOrderStatus();
        if (!OrderStatusEnum.WAITING_FOR_PAYMENT.getCode().equals(orderStatus)) {
            throw new ServiceException(StrUtil.format("该订单: {}所处状态不能支付!",orderNo));
        }

        User user = userService.getById(order.getUserId());


        //自定义交易号
        String outTradeNo = IdUtil.getSnowflake().nextIdStr();

        //组装支付参数
        WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
        orderRequest.setBody("存柜订单:"+orderNo);
        orderRequest.setOutTradeNo(outTradeNo);
        //元转成分
        orderRequest.setTotalFee(BaseWxPayRequest.yuanToFen(StrUtil.toString(order.getPayAmount())));
        orderRequest.setOpenid(user.getOpenId());
        // 获取当前时间
        LocalDateTime currentDateTime = LocalDateTime.now();
        //过期时间
        LocalDateTime expireTime = currentDateTime.plusMinutes(5);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        //设置ip
        orderRequest.setSpbillCreateIp(ip);
        orderRequest.setTimeStart(currentDateTime.format(formatter));
        orderRequest.setTimeExpire(expireTime.format(formatter));
        orderRequest.setTradeType("JSAPI");
        orderRequest.setNotifyUrl(notifyUrl);
        Position position = positionService.getById(order.getPositionId());
        if (position != null && position.getOpenSpilt().equals(OpenSpiltEnum.open.getCode())) {
            //  字段名：是否指定服务商分账.
            //  变量名：profit_sharing
            //  是否必填：否
            //  详情：Y-是，需要分账  N-否，不分账，字母要求大写，不传默认不分账
            //  详细参考 https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=24_3&index=3
            log.info("设置订单：{},需要分账",orderNo);
            orderRequest.setProfitSharing("Y");
        }

        try {
            //创建支付单
            Object payServiceOrder = wxPayService.createOrder(orderRequest);
            //更新订单
            boolean b = orderService.update(Wrappers.<Order>lambdaUpdate().set(Order::getPayWay, OrderPayWayEnum.wx_pay.getCode())
                .set(Order::getPayTime,DateUtil.now())
                .set(Order::getOutTradeNo, outTradeNo).eq(Order::getOrderNo, orderNo));
            if (!b){
                throw new ServiceException(StrUtil.format("订单支付-修改订单状态失败!订单号:{}",orderNo));
            }
            return R.ok(payServiceOrder);
        }catch (Exception e){
            log.error(StrUtil.format("微信支付失败！订单号：{},原因:{}", orderNo, e.getMessage()),e);
            return R.fail("支付失败，请稍后重试！");
        }
    }


    @Override
    public void notify(String xmlData) {
        log.info("支付回调消息:{}",xmlData);
        Boolean flag = false;
        String outTradeNo = null;
        try {
            WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(xmlData);
            outTradeNo = notifyResult.getOutTradeNo();
            String transactionId = notifyResult.getTransactionId();

            Order order = orderService.getOne(Wrappers.<Order>lambdaQuery().eq(Order::getOutTradeNo, outTradeNo));
            if (order.getOrderStatus().equals(OrderStatusEnum.WAITING_FOR_PAYMENT.getCode())){
                flag = orderService.update(Wrappers.<Order>lambdaUpdate()
                    .set(Order::getSuccessTime,DateUtil.format(DateUtil.date(),"yyyy-MM-dd HH:mm:ss"))
                    .set(Order::getTransactionNo, transactionId)
                    .set(Order::getOrderStatus, OrderStatusEnum.IN_USED.getCode())
                    .set(Order::getStartTime, DateUtil.format(DateUtil.date(),"yyyy-MM-dd HH:mm:ss"))
                    .eq(Order::getOutTradeNo, outTradeNo));
            }
        }catch (Exception e){
            log.error("支付回调解析失败!",e);
        }
        try {
            //开锁
            if (flag && StrUtil.isNotBlank(outTradeNo)) {
                machineService.openByOutTradeNo(outTradeNo);

            }
        }catch (Exception e){
            log.error(StrUtil.format("支付回调-开锁失败!,交易号:{}",outTradeNo),e);
        }
    }


}
